How to code
Computers follow specific instructions. As a coder, you can write these instructions and those instructions are called a program.
Table of Contents
Chapter 1: The Command PromptThe first thing you should know about as a beginner is something called the command prompt. This is what it is called on the Windows operating system, on other systems it can be called a terminal emulator. I'll talk about the Windows environment now.
The idea behind it is you can type a command with the keyboard and pressing enter and the computer will respond to that and display words or numbers in text on the screen. Computers in the past had this mode of working and could not display graphics yet, and it was the normal way of working on it. The graphical user interface is considered an improvement on this since you do not have to memorize the commands.
The command prompt is still an efficient way to work with the system when you are doing coding tasks, and it is also useful for other things like network troubleshooting or editting plain text files or working with files in general. You can still use the graphical user interface but knowing how to use the command prompt makes many tasks faster to do.
To open the command prompt, press the Windows key on the keyboard and type cmd.exe and press enter. This will open up the black box that is your command prompt environment. You will see what is called a path that begins with C:\.
C: is a drive letter. Usually C: is the main hard drive. The \ character seperates the folder names. So C:\Users\Yourname\ is the path to the main drive, then the Users folder, then the Yourname folder. The older term for folder is directory. When in the command prompt the folders are often called directories.
So, what commands can you type to make the computer respond?
A good one to be aware of is help.
This will display a list of the other commands it recognizes.
It is a long list, and you don't have to memorize them all, but you will remember the common ones by using them. But if you forget the way to spell the command or what it does, you can review it there.
You can also give what is called an argument to a command.
Try this:
help help
This will display the help information for the help command itself.
You can learn about the other commands this way, typing help copy for example will show you the arguments copy will let you use and how to do it.
This does seem like information overload, but what this means is you don't have to rely on your own memory as long as you know how to get help from the system. You can discover what you can do.
Another good command to know is dir. dir shows the files that are in the current folder, or directory where you currently are.
To change the directory you currently are the command is cd followed by the folder name. cd is short for Change Directory. You can use .. as a folder name which means go one folder up. So if you are in C:\folder1\folder2 and then type cd .. it will make the current directory C:\folder1. You can type just the folder name and not the full path for folders in the current location. So if you type cd folder2 you will find yourself back in C:\folder1\folder2.
You should also be aware of another command called path.
This shows a list of folders, seperated by ; that the system will find programs in to run. Part of the path is C:\Windows\system32. Try dir C:\Windows\System32\*.exe
Files that end in .exe are programs. * is a wildcard which means anything matches as long as it ends in .exe so it will display the files this way.
So to run a program that you create or is already installed by typing its name it will need to be in that list of directories called your path or it can be in the current directory. So you can type notepad and it will run notepad.
You can also run a program with the full path, so you can type c:\myfolder\myprogram.exe and it will run even if it is not on your path.
Paths and files are important in coding, so it's good to understand them and be able to see your files and where they are, and the command prompt is another way to see and interact with files and programs.
If you are using Linux or the Macintosh there will be a different set of commands, and another to get a window to type them into. Try to find out what commands you can use to get more information. On Linux, help will show some commands and others are described with man and info commands. Also those systems do not have a drive letter before the folders in the path. They use a / instead of a \ for the seperator. There are a lot of little differences between things but often the same idea.
Chapter 2: The Lua InterpreterSo now that I have introduced the command prompt, let's talk about how to make other programs. You will now need a tool which makes plain text files into programs called an interpreter or compiler. These are two terms which means how it changes the text file into a program.
An interpreter will follow the steps in the text file and do the actions in them right away.
A compiler will take the steps in the text file and create an executable file, the files that end in .exe and then it can be run without the compiler.
Some of the tools can be a compiler or an interpreter or both. Some will compile into a format it can interpret faster than the plain text file. There are many choices of interpreters and compilers out there and each one will work in its own way. You don't have to know about them all to get started, and what you learn about one thing can often apply to others.
Let's get an interpreter called lua. lua is a fast and small language, with a few powerful and flexible concepts. It's my favorite so I'm going to be teaching that.
I have made a 7z archive of the Windows version of lua with some libraries I have found and written. Here is the link.
Extract that to a folder you choose. You will need 7zip or another program that handles 7z files to extract it, or on Windows 11 you can right click the file to extract 7z.
Now open the command prompt and then cd to the path of the interpreter.
Type lua and press enter.
If you extracted it correct and are in the right folder path, you will see the prompt.
Lua 5.3.0 Copyright (C) 1994-2015 Lua.org, PUC-Rio
>
Here you can type lua code and things will happen.
try:
print "Hello"
It will respond Hello on the next line.
You can also use it like a calculator and put in expressions like 1 + 2 * 10.
You can type ctrl Z and then enter to quit and you'll be back in command prompt.
So now you have an interpreter and you have tested that it is working.
So now let's create a script. Open notepad and then in the file have the same command.
print "Hello"
Save the file as hello.lua and save it in the same folder as lua.exe.
In the command prompt try it out with:
lua hello.lua
It should respond with Hello on the next line.
If you can do that, now you can do coding. You are ready to go. Any list of instuctions can be run in this way. Next part we will go into the specifics of how lua coding works and what you can do.
You may want to put lua.exe into your path so that no matter which folder is your current directory, you can run lua.
To do this in Windows 11, go to your settings app. Then search for environment. There will be an entry that says Edit the environment variables for your account. A window appears and one of the variables you can edit shows Path. If you click that one, and click the Edit... button the list of folders in your path appears and you can add the path to lua.
Another thing you can do if you are not sure where the folder is in your path, in file explorer there is an address bar. If you click it, it becomes editable. Put cmd in the address bar and press enter. It will open the command prompt with the path to the folder you were looking at. Now you can tell where Downloads and other folders actually are in the file system.
Chapter 3: Lua scriptsNow that we have an interpreter and can open up the command prompt to run a program, we can talk about what makes up a program.
The parts of a program are statements, expressions and variables. A statement one line of code. An expression is part of a line which will result in a value. 1 + 1 is an example of an expression. That results in 2. A variable is a name that stores the value.
Let's put a few statements in your program. Make a text file in notepad containing this:
--This is an example program.
print "Hello."
print "What is your name?"
name = io.read()
print ("Hello " .. name)
print "1+1="
print (1+1)
Try to save it and run it and you can see what happens.
The first statement is a statement that does not do anything at all. It is called a comment. Comments begin with --. The interpreter will skip this line. You should use comments in any place where you do something confusing or explain what it is that you're trying to accomplish with the code. Those are there for the benefit of the programmer who is looking at it later.
The next statement is print "Hello." like we saw in the other example. The system responds with the world Hello. exactly as it is quoted. The next line is similiar, it shows What is your name? just like in the quotes. print is a function that will display to the window.
The next line is name = io.read(). name is a variable. A variable is where some data is stored. I'll go over what kinds of variables you can have later, but this one will take the result of io.read() and store it. io.read() is a function that will return the input that gets typed.
The kind of statement with an = is called an assignment statement. That means that the results of the expression on the right side gets stored in the variables on the left side.
The next statement is print ("Hello " .. name). This is print statement like the others, but it has an expression. The .. is an operator, that is to combine two strings. A string is a sequence of text. So this will take the string "Hello " and combine it with the name variable. So, if earlier you typed Alice as your name, now the system will respond Hello Alice.
The next statement is print "1+1=". This is like the others, in that the argument is in quotes and it will print the literal string you told it to.
The last one is print (1+1). This statement will evaluate 1+1. A + does math on numbers, adding them together. The two numbers are 1 and 1, so the result of the expression 1+1 is 2. The system will print 2.
So there were alot of things happening in these simple statements. Feel free to try things and see if you can determine what it will do. If you make a statement that isn't valid lua, the system will give you an error message and stop the program at that point.
Making mistakes like that is completely normal, and finding and correcting the errors is good experience. It takes practice to figure out what the error messages mean, so don't feel bad if there is an error.
It's actually good if there is an error message, since the other kind of mistake is that the program is doing what you wrote, but logically you are not telling it to do what you need to tell it to do to get the result you wanted. It doesn't try to guess what you intended.
Chapter 4: Variable typesLet's talk about variables. Variables use memory, and when a program starts the only variables that exist are the ones that are built into lua. So if the program ends or even if there is an error and it stops, nothing is permanently changed unless it is saved to a file.
There are different types of variables. Here is the list:
A string is a sequence of text. "Alice", "cherry", "22" are examples of strings. When you put strings literals in the program, they're surrounded by quotes. The .. is the string operator, which is called concatenation. Another operator is # which is the length operator. That will return the number of characters in a string. #"Alice" is 5. #"" is 0. Most of the other string tasks are done with the string functions like string.sub.
A number is a value like 1, 14, 3.5, or 0. You don't put quotes around numbers when they are literals in the program. They can be used with math operators like these:
The math operators follow the order of operations, meaning it will do the multiplications and divisions before the additions and subtractions. Parentheses can be used for grouping.
So (4 * 5 + 2 * 10) would be 40, and (4 * (5 + 2) * 10) would be 280.
A boolean can have the values true and false. This is a result of a comparison such as 3 > 4 or name == "Alice". When you compare two items they need to have the same type.
The comparison operators are:
The other operators for booleans are:
This is used to combine conditions. For example:
print(name == "Alice" or name == "Kandy")
That would print true if the name variable had either one of the names, otherwise it's false. These are useful in conditional statements which I'll talk about later.
A nil is the value nil. This is the empty value, so if you use a variable that doesn't have anything stored in it, the value is nil. If you assign a variable nil, this will delete that variable.
A table is a group of other variables. You create it with a {}. I'll go into more details about tables later, but you can use a . to get a value from a table by a string name, or use [] to look it up by another value. The function io.read is actually a function named read that's in the io table.
A function is something you can run. It does a task and then it can return values. print and io.read are functions. You can make your own functions and I will go into detail about that later too.
A thread is used by the co-routine functions. I don't often use these, you can look up those in the manual.
Finally a userdata is like some memory the system is using. You don't set these up with literals. It is sort of a miscellaneous category. File handles, and things like windows, graphics or network sockets are userdata. You can use them in assignments and function calls.
Chapter 5: ConditionalSometimes you want the code to only run if a certain condition is met. The conditional statement is the if statement. Let's type in another example.
print "Hello, what is your name?"
name = io.read()
if name == "Alice" then
print "It's good to see you!"
else
print "I'm looking for Alice, have you seen her?"
end
You might have guessed what this will do, but let's talk about it.
First thing it will ask for your name. That gets stored in the variable called name.
Now is where the conditional statement happens. It will first check if name exactly matches Alice. And if it does it will do the first statement which is to respond It's good to see you!
If it does not match it will proceed to the section after the else. It will say I'm looking for Alice, have you seen her?
The word end marks the end of the if statement. This example uses indenting to show the seperation between the two things it can do, but this indenting isn't required in lua. It will be looking for the word end or else to determine when to stop. A conditional does not have to have an else part, then if the condition is not met, the statements are skipped.
Chapter 6: LoopsThe next thing is how to make the program repeat, and that is with loops. There are a few kinds of loops in lua. The kinds of while, repeat, and for loops.
The while loop repeats as long as a condition is true. If the condition is false, it will never start the loop.
So here is an example of a while loop:
count = 0
while count < 10 do
print(count)
count = count + 1
end
This will print
0
1
2
3
4
5
6
7
8
9
This will start the count at 0, and print and increase the count by 1. Once the count is 10, it will no longer be less than 10 and it will not repeat again.
A repeat loop is similiar to the while loop, except that it will test the condition at the end. So it will run at least once.
Here is an example which does the same thing as the other.
count = 0
repeat
print(count)
count = count + 1
until count > 9
The condition is the opposite of the while loop, it will stop repeating when the condition is true.
The for loop has two types. One of them increases a value.
for count = 0,9 do
print(count)
end
This type of loop is convient when you are doing something a certain number of times.
The other type of for loop is more advanced, it will do something for each thing in a table.
Here is an example of that too:
names = {"Alice", "Hitomi", "Kandy"}
for key, value in ipairs(names) do
print(value)
end
It will respond:
Alice
Hitomi
Kandy
The assignment statement creates a table with three elements. The for loop goes through each one and assigns the value in the table.
Chapter 7: Number guessing gameHere I will do a larger example that combines loops with conditionals, and uses variables.
-- Number Guessing game example
-- by Hitomi Hoshino
math.randomseed(os.time) --initialize random numbers
print "Hello, I will think of a number between 1 and 100."
number = math.random(100)
running = true
while running do
repeat
io.write "Please enter a number. "
guess = io.read()
if not tonumber(guess) then
print "Please enter a valid number."
end
guess = tonumber(guess)
until tonumber(guess)
if guess > number then
print "Your guess is too big."
end
if guess < number then
print "Your guess is too small."
end
if guess == number then
print "You guessed it!"
running = false
end
end
I did put in a few things I didn't introduce before.
math.randomseed and os.time are the functions to get the random number generator ready, and to return the current time in a numeric code. This will set up the random numbers differently every time you run the code. Random numbers in lua are only what's called psuedo-random, they do have a sequence but it's hard to predict. They're good enough for simple games.
I put a comment after the math.randomseed statement. Even though it's on the same line, anything after the -- is just a note for the programmer to read.
math.random returns a number between 1 and the argument. So math.random(100) will return a number up to 100.
io.write is like print, but it doesn't put in a new line after. It's good for prompts and also for writing to files.
tonumber is a function which will change a string to a number if it can. If the string is not a number, it returns nil.
Verifying input is important, if the user makes a mistake and types something that is not a number, the program could have an error message when it gets to the part where it compares the guess, if we didn't have the loop making sure what they typed was a number. In general, you want to make sure any input is valid as soon as you can, so it doesn't cause problems later on.
The repeat loop is repeating until the guess is a number. The conditional statements will take any value except for nil and false as being true. So that means that by testing tonumber(guess) as long as that returns any number, it can stop repeating.
When your guess is equal to the number, it sets a variable named running to false. The loop repeats as long as running is true, so that's how it determines when it's done.
Try to modify the program to keep track of the number of guesses, or have it ask if you want to play again.
Chapter 8: FunctionsNow it is time to talk about functions. We've been using built in lua functions this whole time, but you can create your own functions to call as well.
Functions are actually also stored in variables. print is a function that is stored in a variable called print. You could give a function another name with an assignment like this:
display = print
Then you could use display "Hello" and it would do the same thing as print "Hello" would.
To call a function, as we've seen, we have the function name followed by its arguments. This is usually in parentheses, like this:
print (1+1)
As a shortcut, if your argument is a single string literal, it also allows you to call it like this:
print "Hello "
This is the same thing as calling it like this:
print ("Hello ")
There is a similiar shortcut calling the function with a single table.
table.concat {"apple", "banana", "cherry"}
There is more to say about tables later too, so I'll come back to that.
Here is an example of creating a function:
function greet(name)
print("Hello ," .. name)
end
greet "Alice"
greet "Hitomi"
The first part of the code creates the function. It's set up to take one argument, called name.
It will do the print function and then its done. We call that function twice with different arguments. In this case, it didn't return any value.
The next thing I will need to explain is local variables. Variables we have been using so far are called global variables. That means assigning them replaces the value everywhere in the program. But it's possible to have a local variable, which means you can reuse the same name in a different scope, like in a function, without replacing what is saved in the variable outside. This is easier to see with a code example.
function do_calculation(number1, number2, name)
local result
result = number1 + number2
return name .. result
end
result = 40
print(do_calculation(4, 6, "Alice"))
print("Result is " .. result)
This time, the function takes three arguments and returns one result. number1, number2, and name are the arguments. There is a local variable called result. When it is called, result will be 4 + 6, which evaluates to 10. Then it returns "Alice" with 10 after, so it will display Alice10. However, earlier a global variable called result has 40. But when that is printed it is still 40, because the result variable in the function is local to that function.
When using a local variable, you can also do the first assignment in the same statement, so you could have a line like:
local result = 6
That would set result to be 6 for the current group of statements.
Local variables might seem confusing but using those does free you up to reuse names in functions without worrying about affecting the global variables outside.
The names of the variables used in the argument list of the funtion and in the for loop are also like local variables. They are only being used in that part of the code. If you were to refer to them outside of the function or the loop, they would have nil stored in them instead.
There is another thing to be aware of with functions, and variables and that is multiple assignment. It is possible to assign more than one variable at once, and you can return multiple results from a function. Many of the built in functions will return nil and an error message if something goes wrong.
Here is another example:
function multiple(a, b, c)
return a + 1, b + 2, c + 3
end
apple, banana, cherry = multiple(0, 1, 2)
print("apple is " .. apple)
print("banana is " .. banana)
print("cherry is " .. cherry)
The result of this is that apple is 1, banana is 3, and cherry is 5.
The function returned three results, and the assignment statement had three variable names on the left side. It will assign them in the same order that the return statement returns them.
What happens if there aren't enough variables on the left side? The extra variables get nil assigned to them. If there are more values returned than there are variables, they will just get ignored.
There is also a special case where an function argument is named ...
... is not usually a valid name for a variable, variables are only allowed to begin with a letter and can't have special characters as part of the name, only letters, numbers, and an underscore. But ... means any number of arguments. It's possible then to put ... into a table and refer to them by number instead of by name.
This is another thing that will be easier to give an example of when we get to tables.
There is another use of a local variable that is useful with functions. This kind of local variable is called an upvalue. It is a local variable that is defined before the function that can be used to save a value between function calls. I'll give another example of this:
local count = 0
function increase()
count = count + 1
return count
end
increase()
increase()
increase()
result = increase()
print(result)
This will print 4. So each time increase is called, the count variable goes up by 1. We only used the result of increase in the fourth statement. By making the variable count local, we could have a different count variable above a different function keeping the saved value independantly, without having to decide on another name for the global variable.
The usual way of writing a function assigns the name to it right away, but it's also possible to create a function with an assignment statement without a name.
test = function()
return 42
end
print(test())
That would display 42. It does the same thing as writing it as function test(). Some functions take functions as arguments, so it's helpful to be able to create them without storing them in a variable some times.
Functions are very helpful so you don't have to repeat the same code and over, and are like mini programs themselves. I like to put comments before most functions to tell what arguments they expect to take and what values it will return and what the function is supposed to do. Using good names helps with this as well.
Chapter 9: StringsNow I will go into details about strings.
There are a couple of operators for strings which we've talked about. .. for concatenating strings, and the # operator for getting the length of a string.
Something else to know about the string literals is the backslash character. \ has a special meaning in the strings and to type an actual backspace it must be repeated \\. \n is a newline, \r is a return, \t is a tab. \" is a quote (this is a way to get a quote mark in a string without ending it). This is a way to put a character into a string that you can't actually type easily.
Besides quotes, like " or ' you can also use [[ and ]] to quote a long string. For [[ it does not use the \ escapes and it will allow you to have multiple lines in one string.
poem = [[
Twas brillig, and the slithy toves
Did gyre and gimble in the wabe;
All mimsy were the borogoves,
And the mome raths outgrabe.]]
This will make a big string with four lines. This can also be a way to get quotes and \ characters into the string without having to escape them with \'s.
Other string operations you can use the functions in the string table and I want to talk about a few of them.
A very important one is string.sub. string.sub allows you to get a sub string from a string. string.sub("Alice", 2, 4) will result in "lic". The two numbers are the positions of the characters, first and last. So the secon character through the fourth character. It can also use negative numbers to count from the end of the string. string.sub("Alice", 1, -2) will result in "Alic". You can also leave out the third argument to get the string until the last character, just like using -1. string.sub("Alice", 2) is "lice".
Two other functions that are helpful are string.lower and string.upper. This will change the case of letters in the string. string.lower("Alice") is "alice" and string.upper("Alice") is "ALICE". This is good for comparing strings, even if you don't care what the case is in the original string.
string.find will locate a pattern in a string. For example string.find("Alice", "ice") will return 3, 5. The pattern part "ice" can contain special characters besides the letters to be searched for, those would begin with % or . or (). That would let you find certain types of characters like all numbers, or all letters, or any number of characters before and after another. This is worth looking in the manual to see what all the pattern strings do, since there are quite a few. It takes practice to know what patterns you can use for what situations, but they can make it so you can find some code without having to use alot of comparisons and a loop. Tricky patterns I still like to use comments to explain what's going on.
string.gsub will do a replacement, string.gsub("Banana", "na", "aa") will return "Baaaaa". The search argument uses patterns like string.find, so it can be used to replace strings in a variety of ways. It can also take a function for the third argument.
Chapter 10: FilesNow lets talk about files. Files let you save data which exists after the program is finished, and it can be loaded again.
A very convienent function for dealing with a file line by line is io.lines. It's used in a for loop. For example:
for line in io.lines("inputfile.txt") do
print (line .. "!")
end
If you had a file called inputfile.txt and it contained this:
apple
banana
cherry
The output when you run this would be:
apple!
banana!
cherry!
Each time through the loop it reads a line of the file and assigns it to the variable line. The loop ends when it has read the whole file.
Here are the functions which can write to a file.
First, open the file with io.open.
myfile = io.open("mynewfile.txt", "w")
myfile:write "apple\n"
myfile:write "banana\n"
myfile:write "cherry\n"
myfile:close()
The first argument to io.open is the file name. The second is the mode. "w" is write mode, and "r" is read mode. "a" is append mode. Opening a file in write mode will create the file, and it will replace a file that is already there, meaning it will erase any information that is already there. It returns a file handle, which is a variable that is used to work with the file now.
Now file handle followed by :write will write new lines into the file.
Finally the file handle followed by :close() will close the file. If you forget to close the file, it will close when the program ends, but if you need to open the file to read from it again you'd need to close it before you can do that. It's a good idea to close the file as soon as you are done working in it anyway.
If the file was opened in read mode, you can then use file handle followed by :read() to read a line in. It would return what was read, similiar to how io.read() will get a line that gets typed in. If the file is already at the end, it will return nil.
myfile = io.open("mynewfile.txt", "r")
line = myfile:read()
myfile:close()
print "The first line of the file is:"
print (line)
That would read one line in only.
You can also use :read("a") to read the entire file in instead of just one line.
Chapter 11: TablesTables are collections of other variables.
The simplest way to create one is by using {}. Then data can be put in and taken out of the table with the [] operator. Here is an example:
t = {}
t["alpha"] = "a"
t["beta"] = "b"
t[1] = "c"
t[2] = "d"
print (t["alpha"], t["beta"], t[1], t[2], t[3])
This should show "a" "b" "c" "d" nil
The last result is nil since we never assigned anything to that key in the table.
There is a shortcut for accessing the string keys, and that is with a .
t = {}
t.alpha = "a"
t.beta = "b"
t[1] = "c"
t[2] = "d"
print (t.alpha, t.beta, t[1], t[2], t[3])
This is equivalent. You can't do t.1 or t.2 however since 1 and 2 aren't valid variable names, and those would be string keys rather than number keys. t["1"] and t[1] are not the same.
There is another shortcut when creating tables. Rather than start with an empty table, you can do this:
t = { alpha = "a", beta = "b", [1] = "c", [2] = "d" }
You can assign the initial keys using the string names or with [] around the key.
There is yet another shortcut when setting up a table. Number type keys are automatically used starting with 1 and increasing by 1 each time.
t = { alpha = "a", beta = "b", "c", "d" }
"c" is set up with the key of 1, and "d" is 2.
There are some functions that help with setting up and using tables using the numeric keys that start with 1, and the length operator # also is used with these kinds of tables. # will give you the count of the number keys until it gets to a nil one.
t = { alpha = "a", beta = "b", "c", "d" }
print (#t)
This will print 2, because there are keys for 1 and 2 but not 3.
What if you wanted to do something for each variable stored in a table? We talked about the for loop and there are few ways you can use a for loop to do something for each thing in a table.
Here is an example again:
t = { alpha = "a", beta = "b", "c", "d" }
for key, value in pairs(t) do
print ("key is " .. key .. " value is " .. value)
end
The result:
key is 1 value is c
key is 2 value is d
key is beta value is b
key is alpha value is a
When looping over the keys in a table using pairs, the order is not saved, so it can actually go in any order, not just the order the keys were stored in the table.
There is another function you can call in the for loop call ipairs. This one goes in order but it only uses the number keys until a nil one is found.
t = { alpha = "a", beta = "b", "c", "d" }
for key, value in ipairs(t) do
print ("key is " .. key .. " value is " .. value)
end
The result of that is:
key is 1 value is c
key is 2 value is d
Notice that it doesn't do anything with the other keys at all, only 1 and 2.
Last you can use a for loop with the # operator to get an effect that is alot like using ipairs:
t = { alpha = "a", beta = "b", "c", "d" }
for key = 1, #t do
print ("key is " .. key .. " value is " .. t[key])
end
This doesn't automatically put t[key] into a value variable like using ipairs does.
The functions that use the tables with numeric keys are table.insert, and table.remove.
table.insert will insert keys.
t = {"apple", "banana", "cherry"}
table.insert(t, "donut")
for k, v in ipairs(t) do
print(v)
end
The result here is:
apple
banana
cherry
donut
It inserts the key at the highest key. You can also specify where it will insert it and it will move the other values to the next key. So if you used table.insert(t, 2, "donut") it'd be apple, donut, banana, and cherry in that order.
table.remove is similiar.
t = {"apple", "banana", "cherry"}
removed = table.remove(t)
for k, v in ipairs(t) do
print(v)
end
print ("You removed " .. removed)
This says:
apple
banana
You removed cherry
Similiarly you can use that with a number to remove from a different position. It also returns the value that got removed.
Earlier we were seeing there is a special ... variable you can accept in a function that can take any number of arguments. There is a function called table.pack that will put all the arguments into a table and put the number of them in the n key. This works well with the ... variable which acts like multiple returns. I'll demonstrate what I'm talking about here.
function multiple(...)
local t = table.pack(...)
return t
end
result = multiple("one", "two", "three")
print ("There are " .. result.n .. " arguments.")
for key, value in ipairs(result) do
print ("key:" .. key .. " value:" .. value)
end
This shows:
There are 3 arguments.
key:1 value:one
key:2 value:two
key:3 value:three
There is a table called arg that gets the command line arguments from where you started lua. the key [0] is set to the name of the lua interpreter itself. So if you ran lua as lua myprogram.lua one two
The table would have [0] as "lua.exe", [1] as "myprogram.lua", [2] is "one", and [3] is "two"
This is useful when making command line utilities you can call with filenames or other options.
Another built in table is _G. _G is the table with the global environment, that is all the global variables are entries in this table. This means that _G._G is also the global environment. Try this:
for key, value in pairs(_G) do
print(key)
end
The result is:
math
pcall
assert
rawget
debug
xpcall
loadfile
select
load
string
_G
_VERSION
package
tostring
ipairs
arg
rawset
bit32
dofile
utf8
type
pairs
next
rawequal
coroutine
os
io
collectgarbage
error
getmetatable
rawlen
setmetatable
require
table
tonumber
print
That's all the lua globals there are.
The last thing about tables I am going to talk about here is metatables. Metatables are tables that can assigned to other tables to change how the tables work. One of the tables is the global environment, so using metatables you can change what happens when you access the global environment too. You can use this to make creating globals an error, or to make one table return the values from another table.
This is a powerful, flexible, but potentially confusing thing to do, so use metatables with care since a little bit of it goes a long way. It's a good idea to use comments to describe what you are doing.
--print a message if a global that is not set already does get set
function global_warning()
local mt = getmetatable(_G) or {}
mt.__newindex = function(t, k, v)
print("* Global " .. k .. " set to " .. v)
rawset(t, k, v)
end
setmetatable(_G, mt)
end
Here is an example of a function that if you call it, then if you set a new global variable that hasn't been set befor, a message will be printed out. This could be helpful in tracking down a mispelled variable name.
The first thing it does it get the old metatable of the _G table if there is one, or else creates a new table.
Then it sets the __newindex key, which is the function that runs when a new index is made. The arguments the function takes is the table itself, which would be the global environment, the new variable name, and what it's being set to. It prints out the message and then does rawset, which is does the assignment without the metatable being looked up. Finally it sets the metatable on _G again.
Check the lua manual to see what keys a metatable can use. You can set other keys, but it won't make anything happen. The special keys all begin with __.
Finally there is a special table called _ENV that if you assign a table to it it becomes the global environment from that point forward in the scope. This can be used if you want multiple global environments. It is like ... in that it is not an actual global variable itself, but the language recognizes when you do the assignment.
Chapter 12: LibrariesNext thing to talk about are libraries. Instead of having to write the same code over and over, it makes sense to keep code in different files so you can reuse your code.
The an easy function to run code in another file is the dofile function. This one is pretty simple, dofile "yourfile.lua" will open the file yourfile.lua and run the lua code inside. It will return just like a function call with what your file returns.
--first file
local result = dofile "secondfile.lua"
print ("It was " .. result)
--this part would be in another file called secondfile.lua in the same folder as the first file
return 4 * 7
That would show "It was 28"
You could use dofile in an interactive lua prompt to load in a file too.
The next function I wanted to talk about is package.loadlib. That function will run a dll file written in another language, like C or C++. There are examples of using that in the common.lua file in the runtime. Making a dll library that works with lua is a more advanced topic than what I will get into here, but the fact that it's possible makes lua good for more than just putting messages on the command prompt or writing simple files.
The next function is called require. This is like a combination of dofile and package.loadlib with some rules on what folders it tries to run the code from. Usually require will take the name of the library and return a table with functions. But it's possible for it to do anything. I will go through what libraries I put together in the run time.
It goes through the variables package.path and package.cpath and replaces the ? with the name you are trying to load. Those global variables can be set up in your systems environment variables LUA_PATH, LUA_CPATH and LUA_PATH_5_3 and LUA_CPATH_5_3 and it will replace the global variables. One of the locations it tries is the folder that lua is in, so it helps to have the library files in that folder.
First I will describe the small libraries that I found and included in the run time. The reference documents for these are in the doc folder too, so check those out and see what all you can do with these.
winapi is a library that has a collection of small functions specifically for Windows.
fltk4lua is a binding to FLTK gui functions you can have windows and message dialogs and things like that.
Next one I want to mention is common. common.lua is my library with common lua functions I want to reuse that I wrote, and it also does require or package.loadlib to include some other things, both that I found and that I wrote.
One of them is lua socket. I change the package.cpath variable in common so the way I load in lua socket is to require "common" first. Then the lua socket funtions are in the socket table.
The next is lfs. That's lua file system. Plain lua doesn't include functions for getting folders and getting the attributes of files, so lfs adds that.
lscd is a small dll I wrote in C before I started using lfs, and it brings in commands called ls and cd to change directory and to return a list of files.
lua_sqlite3 is some database functions using sqlite3. Sqlite is a database that stores its tables into a single file, so it's very helpful.
lua_getch adds the ability to get one key at a time from the command prompt instead of having to enter an entire line to read. lua_getkey is similiar except it can return null if a key is not ready. Those also set up ansi mode on the console so that escape characters which change the colors of the letters or change where the characters come in.
The other libraries aren't loaded by common so they'd have to be required seperately.
lua_curses is a wrapper to curses which also lets you control the command prompt window and set up regions and put the characters in place with different color attributes.
And next is my favorite one lisa2. lisa2 is my wrapper to SDL2 which is set of graphics libaries. lisa2main.lua is a wrapper to that which provides a main function and callback functions, opening a window and setting up things like the sound mixer, creating the full screen window, the frame rate manager and making them all quit out correctly.
Here is an example of using lisa2main to make a drawing program.
local lisa2 = require "lisa2main"
function lisa2.init()
screenTexture = lisa2.CreateTexture(lisa2.renderer, lisa2.PIXELFORMAT_RGBA32, lisa2.TEXTUREACCESS_TARGET, lisa2.width, lisa2.height)
lisa2.font_color = {0, 0, 255, 255}
s = ""
x = 0
y = 0
end
function lisa2.draw()
lisa2.RenderCopy(lisa2.renderer, screenTexture, nil, nil)
lisa2.display_text(5, 10, s)
end
function lisa2.event.mouse_motion(event)
local state = event.state
old_x = x
old_y = y
x = event.x
y = event.y
s = "x=" .. x .. " y=" .. y .. " state=" .. state
if state & lisa2.BUTTON_LMASK ~= 0 then
lisa2.SetRenderTarget(lisa2.renderer, screenTexture)
lisa2.SetRenderDrawColor(lisa2.renderer, 255, 255, 255, 255)
lisa2.RenderDrawLine(lisa2.renderer, x, y, old_x, old_y)
lisa2.SetRenderTarget(lisa2.renderer, nil)
elseif state & lisa2.BUTTON_RMASK ~= 0 then
lisa2.SetRenderTarget(lisa2.renderer, screenTexture)
lisa2.SetRenderDrawColor(lisa2.renderer, 0, 0, 0, 255)
lisa2.RenderDrawLine(lisa2.renderer, x, y, old_x, old_y)
lisa2.SetRenderTarget(lisa2.renderer, nil)
end
end
function lisa2.event.key_down(event)
local sym = event.sym
s = "sym="..sym
--pressed s
if sym == 115 then
local ok, err = lisa2.save_texture_to_PNG("mouse2_save.png", lisa2.renderer, screenTexture)
if not ok then
s = "error - " .. err
else
s = "saved mouse2_save.png"
end
end
--pressed l
if sym == 108 then
local texture = lisa2.load_image_to_texture("mouse2_save.png")
if texture then
lisa2.SetRenderTarget(lisa2.renderer, screenTexture)
lisa2.SetRenderDrawColor(lisa2.renderer, 0, 0, 0, 255)
lisa2.RenderClear(lisa2.renderer)
lisa2.RenderCopy(lisa2.renderer, texture, nil, nil)
lisa2.SetRenderTarget(lisa2.renderer, nil)
s = "loaded mouse2_save.png"
else
s = "image load failed"
end
end
end
lisa2.main()
lisa2.init is the first function that gets called by lisa2.main(), it is setting up a texture which is where the drawing will be stored.
lisa2.draw is the drawing function it calls over and over to update the screen each frame. It is copying the texture to the screen and some text with x and y.
lisa2.event.mouse_motion is what it runs when the mouse is moved.
lisa2.event.key_down is what runs when you push a key.
Those event functions get passed an event table which contains where the mouse is moved, what key is pressed and so on.
lisa2.main is what runs the loop which calls the other functions back. It takes over until escape is pressed, or alt-F4.
This all all you need to make a simple game in lua. If you run it with lua53w.exe instead of lua.exe it will even run without setting up a console window first.
Now you know what all these .dll files are for.
The last one I will talk about is guilua.exe. This one is instead of a seperate dll file to be loaded in with require, it is a seperate program that has some windows calls built in, and it uses lua as its scripting. When you run it, it runs init.lua and sets up a window that lets you add in buttons and lists that can do anything you want it to do. Load in the file in the doc folder called global reference.guilua and you can see a list of every global variable it has, and an example of gui lua itself.
Chapter 13: Conclusion and TipsThis is the end of my lua tutorial, but just the beginning of what you can accomplish with coding.
My tips:
Write some programs. If you are not sure what a function does, or how something works, make a short program to try it out. That is often easier than trying to fix something inside a large program where other code is in the way.
Use the print statement. If you are not sure what your code is actually doing, put in a print statement and print out what is in the variable so you can see something happen step by step. It might make the problem and solution obvious.
Read the documentation. Lua comes with a pretty short manual as far as these things go, and the libaries have documentation too which I included, or wrote if it was one that I wrote. Then you can get familiar with all the things you can do. I went over alot of lua but not everything. You might find that something you were trying to do is already covered in a function you didn't know about. This works well with the first tip of writing some programs. Read the documentation, and try out things. Searching on google for an answer is a good thing to do sometimes too, but at least try to read the manual pages first so it will start to become more familiar.
Also read the book. There is a book called Programming in Lua that is written by the authors of lua that is probably a better book than what I'm writing. It's worth it to read it through.
So next thing is what about other languages? There is more out there than just lua. If you learn to code in lua, you can then learn other languages more easily because you know what variables, loops, and functions are.
It might be that lua is all you need. Since it is a simple and minimal language, that is efficient and easy to understand, you could keep practicing that.
There is the C programming language. It's a smaller language too, but it is trickier. If you make mistakes in C programs could crash instead of giving you a friendly error message. Or worse, it could do the wrong thing entirely, certain kinds of errors can cause mysterious things to happen, if you break the rules of C you get what they call undefined behavior instead of what you intended. But it does have a compiler so you can use it to make dll libraries or exe programs.
Javascript is another interpreted language, it's included in the web browser, and also has its own environments like node or electron. It's more complicated and messy than lua, but it's pretty popular and you can probably get help with it easily.
Python is a very popular interpreted language. It's bigger, having more built in functions and more complicated behavior in its objects. It's not quite as efficent as lua but people seem to like it.
C# and Java are compiled languages that have a very large standard library and are backed by big corporations. It seems these are more for serious work, and they're also a little safer to use than C since they run in environments that make mistakes close the program or show a message rather than corrupting memory and making the wrong thing happen.
When trying out a new language, try to get it's compiler or interpreter working and display a message. It's traditional to write the message "Hello World".
So have a lot of fun. Coding is useful and its fun, usually. When it's not you're learning. Keep trying, don't give up. I hope I have helped you learn more about coding in lua and in general.
Back to my page